use wgpu::{Instance, SurfaceTarget, SurfaceConfiguration, Surface, Device, Queue};
pub use wgpu::{Color, LoadOp, StoreOp};

pub struct Pixmap<'a> {
    device: Device,
    queue: Queue,
    surface: Surface<'a>,
    config: SurfaceConfiguration,

    pub store_op: StoreOp,
    pub load_op: LoadOp<Color>,
}

impl Pixmap<'_> {
    /// create a canvas with a target to draw on
    /// async is required for wgpu initialtion
    pub async fn new_for_target<'window>(
        target: impl Into<SurfaceTarget<'window>>,
        width: u32,
        height: u32
    ) -> Pixmap<'window> {
        let instance = Instance::new(Default::default());
        let surface = instance.create_surface(target).unwrap();
        let adapter = instance.request_adapter(
            &wgpu::RequestAdapterOptions {
                power_preference: wgpu::PowerPreference::default(),
                compatible_surface: Some(&surface),
                force_fallback_adapter: false,
            }
        ).await.unwrap();
        let (device, queue) = adapter.request_device(
            &wgpu::DeviceDescriptor {
                required_features: wgpu::Features::empty(),
                required_limits: if cfg!(target_arch = "wasm32") {
                    wgpu::Limits::downlevel_webgl2_defaults()
                } else {
                    wgpu::Limits::default()
                },
                label: None,
            },
            None,
        ).await.unwrap();
        let config = surface.get_default_config(&adapter, width, height).unwrap();
        surface.configure(&device, &config);
        
        Pixmap {
            device, queue, surface, config,
            store_op: StoreOp::Store, load_op: LoadOp::Load
        }
    }


    /// resizing is usually cheaper than creating a new canvas
    pub fn resize(&mut self, width: u32, height: u32) {
        self.config.width = width;
        self.config.height = height;
        self.surface.configure(&self.device, &self.config);
    }

    /// this crate follows the design of wgpu, submit all the drawing command at the end.
    pub fn render(&mut self) {
        let output = self.surface.get_current_texture().unwrap();
        let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
        let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
            label: Some("Render Encoder"),
        });
        encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
            label: Some("Render Pass"),
            color_attachments: &[Some(wgpu::RenderPassColorAttachment {
                view: &view,
                resolve_target: None,
                ops: wgpu::Operations {
                    load: self.load_op,
                    store: self.store_op,
                },
            })],
            depth_stencil_attachment: None,
            occlusion_query_set: None,
            timestamp_writes: None,
        });
        self.queue.submit(std::iter::once(encoder.finish()));
        output.present();
    }
}